home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / wtek0693.zip / BITMAP.ZIP / DDLIST.C < prev    next >
C/C++ Source or Header  |  1992-10-09  |  30KB  |  996 lines

  1. /*
  2.  * DDLIST.C
  3.  *
  4.  * Main entry code for application demonstrating owner-drawn listbox
  5.  * code to emulate the Common Dialogs drive and directory listboxes.
  6.  * The code uses bitmaps provided in the Windows 3.1 Software Development
  7.  * Kit that come directly from the Common Dialogs themselves.
  8.  *
  9.  * Copyright (c)1992 Kraig Brockschmidt, All Right Reserved
  10.  * Compuserve:  70750,2344
  11.  * Internet  :  kraigb@microsoft.com
  12.  */
  13.  
  14.  
  15. #include <windows.h>
  16. #include <dos.h>
  17. #include <stdlib.h>
  18. #include <stdio.h>
  19. #include <direct.h>
  20. #include <string.h>
  21. #include "ddlist.h"
  22.  
  23.  
  24. //Caches of the bitmaps in our resources.
  25. HBITMAP rghBmpDrives[5];
  26. HBITMAP rghBmpFolders[3];
  27.  
  28.  
  29. /*
  30.  * WinMain
  31.  *
  32.  * Purpose:
  33.  *  Main entry point of application.   Should register the app class
  34.  *  if a previous instance has not done so and do any other one-time
  35.  *  initializations.
  36.  *
  37.  * Parameters:
  38.  *  Standard
  39.  *
  40.  * Return Value:
  41.  *  Value to return to Windows--termination code.
  42.  */
  43.  
  44. int PASCAL WinMain (HANDLE hInst, HANDLE hPrevInst, LPSTR pszCmd, int nCmdShow)
  45.     {
  46.     WNDCLASS        wc;
  47.     HWND            hWnd;
  48.     MSG             msg;
  49.  
  50.     if (!hPrevInst)
  51.         {
  52.         //Main window class
  53.         wc.style          = CS_HREDRAW | CS_VREDRAW;
  54.         wc.lpfnWndProc    = DDListWndProc;
  55.         wc.cbClsExtra     = 0;
  56.         wc.cbWndExtra     = 0;
  57.         wc.hInstance      = hInst;
  58.         wc.hIcon          = LoadIcon(hInst, "Icon");
  59.         wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
  60.         wc.hbrBackground  = COLOR_WINDOW+1;
  61.         wc.lpszMenuName   = MAKEINTRESOURCE(IDR_MENU);
  62.         wc.lpszClassName  = "DDLIST";
  63.  
  64.         if (!RegisterClass(&wc))
  65.             return FALSE;
  66.         }
  67.  
  68.  
  69.     hWnd=CreateWindow("DDLIST", "Common Dialogs Owner-Drawn Listboxes"
  70.                       , WS_MINIMIZEBOX | WS_OVERLAPPEDWINDOW
  71.                       , 35, 35, 350, 150
  72.                       , NULL, NULL, hInst, NULL);
  73.  
  74.     ShowWindow(hWnd, nCmdShow);
  75.     UpdateWindow(hWnd);
  76.  
  77.     while (GetMessage(&msg, NULL, 0,0 ))
  78.         {
  79.         TranslateMessage(&msg);
  80.         DispatchMessage(&msg);
  81.         }
  82.  
  83.     return msg.wParam;
  84.     }
  85.  
  86.  
  87.  
  88.  
  89.  
  90.  
  91. /*
  92.  * DDListWndProc
  93.  *
  94.  * Purpose:
  95.  *  Window class procedure.  Standard callback.
  96.  *
  97.  * Parameters:
  98.  *  Standard.
  99.  *
  100.  * Return Value:
  101.  *  Standard.
  102.  */
  103.  
  104. LONG FAR PASCAL DDListWndProc(HWND hWnd, UINT iMsg, UINT wParam, LONG lParam)
  105.     {
  106.     static HINSTANCE    hInst;
  107.     FARPROC             pfn;
  108.     UINT                i;
  109.  
  110.     /*
  111.      * These three variables and the unpacking code below compiles correctly
  112.      * for both Windows 3.x and Windows NT
  113.      */
  114.     WORD            wID;
  115.     WORD            wCode;
  116.     HWND            hWndMsg;
  117.  
  118.     hWndMsg=(HWND)(UINT)lParam;
  119.     wID=LOWORD(wParam);
  120.     #ifdef WIN32
  121.         wCode=HIWORD(wParam);
  122.     #else
  123.         wCode=HIWORD(lParam);
  124.     #endif
  125.  
  126.  
  127.     switch (iMsg)
  128.         {
  129.         case WM_CREATE:
  130.             //Save the instance handle for creating the dialog box
  131.             hInst=((LPCREATESTRUCT)lParam)->hInstance;
  132.  
  133.             //Load the drive and folder bitmaps for the lists.
  134.             for (i=IDB_DRIVEMIN; i<=IDB_DRIVEMAX; i++)
  135.                 rghBmpDrives[i-IDB_DRIVEMIN]=LoadBitmap(hInst, MAKEINTRESOURCE(i));
  136.  
  137.             for (i=IDB_FOLDERMIN; i<=IDB_FOLDERMAX; i++)
  138.                 rghBmpFolders[i-IDB_FOLDERMIN]=LoadBitmap(hInst, MAKEINTRESOURCE(i));
  139.  
  140.             return 0L;
  141.  
  142.         case WM_DESTROY:
  143.             //Free up our allocated resources.
  144.             for (i=IDB_DRIVEMIN; i<=IDB_DRIVEMAX; i++)
  145.                 DeleteObject(rghBmpDrives[i-IDB_DRIVEMIN]);
  146.  
  147.             for (i=IDB_FOLDERMIN; i<=IDB_FOLDERMAX; i++)
  148.                 DeleteObject(rghBmpFolders[i-IDB_FOLDERMIN]);
  149.  
  150.             PostQuitMessage(0);
  151.             break;
  152.  
  153.         case WM_COMMAND:
  154.             switch (wID)
  155.                 {
  156.                 case IDM_DIALOG:
  157.                     pfn=MakeProcInstance(DDListDialogProc, hInst);
  158.                     DialogBox(hInst, MAKEINTRESOURCE(IDD_DDLIST), hWnd, pfn);
  159.                     FreeProcInstance(pfn);
  160.                     break;
  161.                 }
  162.  
  163.         default:
  164.             return (DefWindowProc(hWnd, iMsg, wParam, lParam));
  165.         }
  166.  
  167.     return 0L;
  168.     }
  169.  
  170.  
  171.  
  172.  
  173.  
  174. /*
  175.  * DDListDialogProc
  176.  *
  177.  * Purpose:
  178.  *  Handles the dialog that displays a drive and directory owner-draw
  179.  *  combobox and listbox, respectiviely.  This procedure handles only
  180.  *  the WM_INITDIALOG and WM_DRAWITEM messages,dispatching them to
  181.  *  alternate functions that handle each message for each list.
  182.  *
  183.  * Parameters:
  184.  *  Standard
  185.  *
  186.  * Return Value:
  187.  *  Standard
  188.  */
  189.  
  190. BOOL FAR PASCAL DDListDialogProc(HWND hDlg, UINT iMsg, UINT wParam, LONG lParam)
  191.     {
  192.  
  193.     char            szCurDir[_MAX_PATH];
  194.  
  195.     /*
  196.      * These three variables and the unpacking code below compiles correctly
  197.      * for both Windows 3.x and Windows   NT
  198.      */
  199.     WORD            wID;
  200.     WORD            wCode;
  201.     HWND            hWndMsg;
  202.  
  203.     hWndMsg=(HWND)(UINT)lParam;
  204.     wID=LOWORD(wParam);
  205.     #ifdef WIN32
  206.         wCode=HIWORD(wParam);
  207.     #else
  208.         wCode=HIWORD(lParam);
  209.     #endif
  210.  
  211.  
  212.     switch (iMsg)
  213.         {
  214.         case WM_INITDIALOG:
  215.             ShowWindow(hDlg, SW_SHOW);
  216.             UpdateWindow(hDlg);
  217.  
  218.             //Initialize the drive combobox with the available drives
  219.             DriveListInitialize(GetDlgItem(hDlg, ID_DRIVELIST)
  220.                                 , GetDlgItem(hDlg, ID_TEMPLIST));
  221.  
  222.             /*
  223.              * Initializethe directory listbox with the current working
  224.              * directory.  WARNING:  The C Run-Time cwd function is not
  225.              * always compatible with Windows DLLs.  If  you are placing
  226.              * this code in a DLL you may need to write your own version
  227.              * using DOS3Call.  We also trust _getcwd to give us a drive
  228.              * letter at the front of the string.
  229.              */
  230.             _getcwd(szCurDir, _MAX_PATH);
  231.             DirectoryListInitialize(GetDlgItem(hDlg, ID_DIRECTORYLIST)
  232.                                     , GetDlgItem(hDlg, ID_TEMPLIST), szCurDir);
  233.  
  234.             return TRUE;
  235.  
  236.  
  237.         case WM_MEASUREITEM:
  238.             {
  239.             static int              cyItem=-1;      //Height of a listbox item
  240.             LPMEASUREITEMSTRUCT     pMI;
  241.  
  242.             pMI=(LPMEASUREITEMSTRUCT)lParam;
  243.  
  244.             if (-1==cyItem)
  245.                 {
  246.                 HFONT       hFont;
  247.                 HDC         hDC;
  248.                 TEXTMETRIC  tm;
  249.                 BITMAP      bm;
  250.  
  251.                 /*
  252.                  * Attempt to get the font of the dialog. However,
  253.                  * on the first attempt in the life of the dialog,
  254.                  * this could fail; in that case use the system font.
  255.                  */
  256.                 hFont=(HANDLE)(UINT)SendMessage(hDlg, WM_GETFONT, 0, 0L);
  257.  
  258.                 if (NULL==hFont)
  259.                     hFont=GetStockObject(SYSTEM_FONT);
  260.  
  261.                 hDC=GetDC(hDlg);
  262.                 hFont=SelectObject(hDC, hFont);
  263.  
  264.                 /*
  265.                  * Item height is the maximum of the font height and the
  266.                  * bitmap height.  We know that all bitmaps are the same
  267.                  * size, so we just get information for one of them.
  268.                  */
  269.                 GetTextMetrics(hDC, &tm);
  270.                 GetObject(rghBmpFolders[0], sizeof(bm), &bm);
  271.                 cyItem=max(bm.bmHeight, tm.tmHeight);
  272.  
  273.                 ReleaseDC(hDlg, hDC);
  274.                 }
  275.  
  276.             pMI->itemHeight=cyItem;
  277.             }
  278.             break;
  279.  
  280.  
  281.         case WM_DRAWITEM:
  282.             //Go draw the appropriate item and bitmap.
  283.             DriveDirDrawItem((LPDRAWITEMSTRUCT)lParam, (ID_DRIVELIST==wID));
  284.  
  285.             //Prevent default actions in listboxes (drawing focus rect)
  286.             return TRUE;
  287.  
  288.  
  289.         case WM_COMMAND:
  290.             switch (wID)
  291.                 {
  292.                 case ID_DIRECTORYLIST:
  293.                     if (LBN_DBLCLK==wCode)
  294.                         {
  295.                         UINT        i;
  296.                         DWORD       dw;
  297.                         char        szDir[_MAX_PATH];
  298.                         LPSTR       psz;
  299.  
  300.                         /*
  301.                          * On double-click, change directory and reinit the
  302.                          * listbox.  But all we stored in the string was
  303.                          * the directory's single name, so we use the bitmap
  304.                          * type to tell if we're below the current directory
  305.                          * (in which case we just chdir to our name) or above
  306.                          * (in which case we prepend "..\"'s as necessary.
  307.                          */
  308.                         i=(UINT)SendMessage(hWndMsg, LB_GETCURSEL, 0, 0L);
  309.                         dw=SendMessage(hWndMsg, LB_GETITEMDATA, i, 0L);
  310.  
  311.                         /*
  312.                          * If out bitmap is IDB_FOLDERCLOSED or the root,
  313.                          * then just .  If we're IDB_FOLDEROPENSELECT,
  314.                          * don't do anything.  If we're IDB_FOLDEROPEN then
  315.                          * we get the full current path and truncate it
  316.                          * after the directory to which we're switching.
  317.                          */
  318.                         if (IDB_FOLDEROPENSELECT==HIWORD(dw))
  319.                             break;
  320.  
  321.                         //Get get the directory for sub-directory changes.
  322.                         SendMessage(hWndMsg, LB_GETTEXT, i, (LONG)(LPSTR)szCurDir);
  323.  
  324.                         if (IDB_FOLDEROPEN==HIWORD(dw) && 0!=i)
  325.                             {
  326.                             //Get the current path and find us in this path
  327.                             GetWindowText(hWndMsg, szDir, sizeof(szDir));
  328.                             psz=_fstrstr(szDir, szCurDir);
  329.  
  330.                             //Null terminate right after us.
  331.                             *(psz+lstrlen(szCurDir))=0;
  332.  
  333.                             //Get this new directory in the right place
  334.                             lstrcpy(szCurDir, szDir);
  335.                             }
  336.  
  337.                         //chdir has a nice way of validating for us.
  338.                         if (0==_chdir(szCurDir))
  339.                             {
  340.                             //Get the new full path.
  341.                             _getcwd(szCurDir, _MAX_PATH);
  342.  
  343.                             DirectoryListInitialize(hWndMsg
  344.                                 , GetDlgItem(hDlg, ID_TEMPLIST), szCurDir);
  345.                             }
  346.                         }
  347.                     break;
  348.  
  349.  
  350.                 case ID_DRIVELIST:
  351.                     if (CBN_SELCHANGE==wCode)
  352.                         {
  353.                         UINT        i, iCurDrive;
  354.                         char        szDrive[18]; //Enough for drive:volume
  355.  
  356.                         //Get the first letter in the current selection
  357.                         i=(UINT)SendMessage(hWndMsg, CB_GETCURSEL, 0, 0L);
  358.                         SendMessage(hWndMsg, CB_GETLBTEXT
  359.                                     , i, (LONG)(LPSTR)szDrive);
  360.  
  361.                         iCurDrive=_getdrive();  //Save in case of restore
  362.  
  363.                         /*
  364.                          * Attempt to set the drive and get the current
  365.                          * directory on it.  Both must work for the change
  366.                          * to be certain.  If we are certain, reinitialize
  367.                          * the directories.  Note that we depend on drives
  368.                          * stored as lower case in the combobox.
  369.                          */
  370.  
  371.                         if (0==_chdrive((int)(szDrive[0]-'a'+1))
  372.                             && NULL!=_getcwd(szCurDir, _MAX_PATH))
  373.                             {
  374.                             DirectoryListInitialize(
  375.                                 GetDlgItem(hDlg, ID_DIRECTORYLIST),
  376.                                 GetDlgItem(hDlg, ID_TEMPLIST), szCurDir);
  377.  
  378.                             //Insure that the root is visible (UI guideline)
  379.                             SendDlgItemMessage(hDlg, ID_DIRECTORYLIST
  380.                                                , LB_SETTOPINDEX, 0, 0L);
  381.  
  382.                             break;
  383.                             }
  384.  
  385.                         //Changing drives failed so restore drive and selection
  386.                         _chdrive((int)iCurDrive);
  387.  
  388.                         wsprintf(szDrive, "%c:", (char)(iCurDrive+'a'-1));
  389.                         i=(UINT)SendMessage(hWndMsg, CB_SELECTSTRING
  390.                                             , (WPARAM)-1, (LONG)(LPSTR)szDrive);
  391.                         }
  392.  
  393.                     break;
  394.  
  395.                 case IDOK:
  396.                     EndDialog(hDlg, TRUE);
  397.                     break;
  398.                 }
  399.             break;
  400.  
  401.         default:
  402.             break;
  403.         }
  404.  
  405.     return FALSE;
  406.     }
  407.  
  408.  
  409.  
  410.  
  411.  
  412.  
  413.  
  414. /*
  415.  * DriveListInitialize
  416.  *
  417.  * Purpose:
  418.  *  Resets and fills the given combobox with a list of current
  419.  *  drives.  The type of drive is stored with the item data.
  420.  *
  421.  * Parameters:
  422.  *  hList           HWND of the combobox to fill.
  423.  *  hTempList       HWND to use for finding drives.
  424.  *
  425.  * Return Value:
  426.  *  None
  427.  */
  428.  
  429. void DriveListInitialize(HWND hList, HWND hTempList)
  430.     {
  431.     struct find_t   fi;
  432.     char            ch;
  433.     UINT            i, iItem;
  434.     UINT            cch, cItems;
  435.     UINT            iDrive, iType;
  436.     UINT            iCurDrive;
  437.     char            szDrive[10];
  438.     char            szNet[64];
  439.     char            szItem[26];
  440.  
  441.     if (NULL==hList)
  442.         return;
  443.  
  444.     //Clear out all the lists.
  445.     SendMessage(hList,     CB_RESETCONTENT, 0, 0L);
  446.     SendMessage(hTempList, LB_RESETCONTENT, 0, 0L);
  447.  
  448.     //Get available drive letters in the temp list
  449.     SendMessage(hTempList, LB_DIR, DDL_DRIVES | DDL_EXCLUSIVE, (LONG)(LPSTR)"*");
  450.  
  451.     iCurDrive=_getdrive()-1;       //Fix for zero-based drive indexing
  452.  
  453.     /*
  454.      * Walk through the list of drives, parsing off the "[-" and "-]"
  455.      * For each drive letter parsed, add a string to the combobox
  456.      * composed of the drive letter and the volume label.  We then
  457.      * determine the drive type and add that information as the item data.
  458.      */
  459.     cItems=(int)SendMessage(hTempList, LB_GETCOUNT, 0, 0L);
  460.  
  461.     for (i=0; i<cItems;  i++)
  462.         {
  463.         SendMessage(hTempList, LB_GETTEXT, i, (LONG)(LPSTR)szDrive);
  464.  
  465.         //Insure lowercase drive letters
  466.         AnsiLower(szDrive);
  467.         iDrive=szDrive[2]-'a';
  468.         iType=DriveType(iDrive);        //See Below
  469.  
  470.         if (iType < 2)                  //Skip non-existent drive B's
  471.             continue;
  472.  
  473.         //Start the item string with the drive letter, color, and two spaces
  474.         wsprintf(szItem, "%c%s", szDrive[2], (LPSTR)":  ");
  475.  
  476.         /*
  477.          * For fixed or ram disks, append the volume label which we find
  478.          * using _dos_findfirst and attribute _A_VOLID.
  479.          */
  480.         if (DRIVE_FIXED==iType || DRIVE_RAM==iType)
  481.             {
  482.             wsprintf(szDrive, "%c:\\*.*", szDrive[2]);
  483.  
  484.             if (0==_dos_findfirst(szDrive, _A_VOLID, &fi))
  485.                 {
  486.                 //Convert volume to lowercase and strip any . in the name.
  487.                 AnsiLower(fi.name);
  488.  
  489.                 //If a period exists, it has to be in position 8, so clear it.
  490.                 ch=fi.name[8];
  491.                 fi.name[8]=0;
  492.                 lstrcat(szItem, fi.name);
  493.  
  494.                 //If we did have a broken volume name, append the last 3 chars
  495.                 if ('.'==ch)
  496.                     lstrcat(szItem, &fi.name[9]);
  497.                 }
  498.             }
  499.  
  500.         //For network drives, go grab the \\server\share for it.
  501.         if (DRIVE_REMOTE==iType)
  502.             {
  503.             szNet[0]=0;
  504.             cch=sizeof(szNet);
  505.  
  506.             wsprintf(szDrive, "%c:", szDrive[2]);
  507.             AnsiUpper(szDrive);
  508.  
  509.             WNetGetConnection((LPSTR)szDrive, (LPSTR)szNet, &cch);
  510.             AnsiLower(szNet);
  511.  
  512.             lstrcat(szItem, szNet);
  513.             }
  514.  
  515.         iItem=(int)SendMessage(hList, CB_ADDSTRING, 0, (LONG)(LPSTR)szItem);
  516.         SendMessage(hList, CB_SETITEMDATA, iItem, MAKELONG(iDrive, iType));
  517.  
  518.         if (iDrive==iCurDrive)
  519.             SendMessage(hList, CB_SETCURSEL, iItem, 0L);
  520.         }
  521.  
  522.     return;
  523.     }
  524.  
  525.  
  526.  
  527.  
  528.  
  529.  
  530. /*
  531.  * DriveType
  532.  *
  533.  * Purpose:
  534.  *  Augments the Windows API GetDriveType with a call to the CD-ROM
  535.  *  extensions to determine if a drive is a floppy, hard disk, CD-ROM,
  536.  *  RAM-drive, or networked  drive.
  537.  *
  538.  * Parameters:
  539.  *  iDrive          UINT containing the zero-based drive index
  540.  *
  541.  * Return Value:
  542.  *  UINT            One of the following values describing the drive:
  543.  *                  DRIVE_FLOPPY, DRIVE_HARD, DRIVE_CDROM, DRIVE_RAM,
  544.  *                  DRIVE_NETWORK.
  545.  */
  546.  
  547. UINT DriveType(UINT iDrive)
  548.     {
  549.     int     iType;
  550.     BOOL    fCDROM=FALSE;
  551.     BOOL    fRAM=FALSE;
  552.  
  553.  
  554.     //Validate possible drive indices
  555.     if (0 > iDrive  || 25 < iDrive)
  556.         return 0xFFFF;
  557.  
  558.     iType=GetDriveType(iDrive);
  559.  
  560.     /*
  561.      * Under Windows NT, GetDriveType returns complete information
  562.      * not provided under Windows 3.x which we now get through other
  563.      * means.
  564.      */
  565.    #ifdef WIN32
  566.     return iType;
  567.    #else
  568.     //Check for CDROM on FIXED and REMOTE drives only
  569.     if (DRIVE_FIXED==iType || DRIVE_REMOTE==iType)
  570.         {
  571.         _asm
  572.             {
  573.             mov     ax,1500h        //Check if MSCDEX exists
  574.             xor     bx,bx
  575.             int     2fh
  576.  
  577.             or      bx,bx           //BX unchanged if MSCDEX is not around
  578.             jz      CheckRAMDrive   //No?  Go check for RAM drive.
  579.  
  580.             mov     ax,150Bh        //Check if drive is using CD driver
  581.             mov     cx,iDrive
  582.             int     2fh
  583.  
  584.             mov     fCDROM,ax       //AX if the CD-ROM flag
  585.             or      ax,ax
  586.             jnz     Exit            //Leave if we found a CD-ROM drive.
  587.  
  588.             CheckRAMDrive:
  589.             }
  590.         }
  591.  
  592.     //Check for RAM drives on FIXED disks only.
  593.     if (DRIVE_FIXED==iType)
  594.         {
  595.         /*
  596.          * Check for RAM drive is done by reading the boot sector and
  597.          * looking at the number of FATs.  Ramdisks only have 1 while
  598.          * all others have 2.
  599.          */
  600.         _asm
  601.             {
  602.             push    ds
  603.  
  604.             mov     bx,ss
  605.             mov     ds,bx
  606.  
  607.             sub     sp,0200h            //Reserve 512 bytes to read a sector
  608.             mov     bx,sp               //and point BX there.
  609.  
  610.             mov     ax,iDrive           //Read the boot sector of the drive
  611.             mov     cx,1
  612.             xor     dx,dx
  613.  
  614.             int     25h
  615.             add     sp,2                //Int 25h requires our stack cleanup.
  616.             jc      DriveNotRAM
  617.  
  618.             mov     bx,sp
  619.             cmp     ss:[bx+15h],0f8h    //Reverify fixed disk
  620.             jne     DriveNotRAM
  621.             cmp     ss:[bx+10h],1       //Check if there's only one FATs
  622.             jne     DriveNotRAM
  623.             mov     fRAM,1
  624.  
  625.             DriveNotRAM:
  626.             add     sp,0200h
  627.             pop     ds
  628.  
  629.             Exit:
  630.             //Leave fRAM untouched  it's FALSE by default.
  631.             }
  632.         }
  633.  
  634.     /*
  635.      * If either CD-ROM or RAM drive flags are set, return privately
  636.      * defined flags for them (outside of Win32).  Otherwise return
  637.      * the type given from GetDriveType.
  638.      */
  639.  
  640.     if (fCDROM)
  641.         return DRIVE_CDROM;
  642.  
  643.     if (fRAM)
  644.         return DRIVE_RAM;
  645.  
  646.     //Drive B on a one drive system returns < 2 from GetDriveType.
  647.     return iType;
  648.    #endif
  649.     }
  650.  
  651.  
  652.  
  653.  
  654.  
  655.  
  656. /*
  657.  * DirectoryListInitialize
  658.  *
  659.  * Purpose:
  660.  *  Initializes strings in a listbox given a directory path.  The first
  661.  *  string in the listbox is the drive letter.  The remaining items are
  662.  *  each directory in the path completed with a listing of all sub-
  663.  *  directories under the last directory in the path.
  664.  *
  665.  * Parameters:
  666.  *  hList           HWND of the listbox to fill.
  667.  *  hTempList       HWND of a listbox to use for  directory enumerations.
  668.  *  pszDir          LPSTR of the path to use in initialization assumed
  669.  *                  to be at least _MAX_DIR long.
  670.  *
  671.  * Return Value:
  672.  *  None
  673.  */
  674.  
  675. void DirectoryListInitialize(HWND hList, HWND hTempList, LPSTR pszDir)
  676.     {
  677.     LPSTR       psz, pszLast;
  678.     char        ch;
  679.     char        szDir[_MAX_DIR];
  680.     UINT        cch, cItems;
  681.     UINT        i, iItem, iIndent;
  682.     BOOL        fFirst=TRUE;
  683.  
  684.     if (NULL==hList || NULL==pszDir)
  685.         return;
  686.  
  687.     //If the path ends in a \, strip the '\'
  688.     cch=lstrlen(pszDir);
  689.  
  690.     if ('\\'==pszDir[cch-1])
  691.         pszDir[cch-1]=0;
  692.  
  693.     //Clear out all the lists.
  694.     SendMessage(hList,     WM_SETREDRAW,    FALSE, 0L);
  695.     SendMessage(hList,     LB_RESETCONTENT, 0,     0L);
  696.     SendMessage(hTempList, LB_RESETCONTENT, 0,     0L);
  697.  
  698.     /*
  699.      * Walk through the path one \ at a time.  At each one found,
  700.      * we add the string composed of the characters between it and
  701.      * the last \ found.  We also make sure everything is lower case.
  702.      */
  703.  
  704.     pszLast=AnsiLower(pszDir);
  705.  
  706.     //Save this for changing directories
  707.     SetWindowText(hList, pszDir);
  708.  
  709.     //Save the directory appended with \*.*
  710.     wsprintf(szDir, "%s\\*.*", pszDir);
  711.  
  712.     while (TRUE)
  713.         {
  714.         psz=_fstrchr(pszLast, '\\');
  715.  
  716.         if (NULL!=psz)
  717.             {
  718.             /*
  719.              * Save the character here so we can NULL terminate.  If this
  720.              * if the first entry, it's a drive root, so keep the \
  721.              */
  722.  
  723.             if (fFirst)
  724.                 ch=*(++psz);
  725.             else
  726.                 ch=*psz;
  727.  
  728.             *psz=0;
  729.             }
  730.         else
  731.             {
  732.             //If we're looking at a drive only, then append a backslash
  733.             if (pszLast==pszDir && fFirst)
  734.                 lstrcat(pszLast, "\\");
  735.             }
  736.  
  737.         //Add the drive string--includes the last one where psz==NULL
  738.         iItem=(UINT)SendMessage(hList, LB_ADDSTRING, 0, (LONG)pszLast);
  739.  
  740.         /*
  741.          * The item data here has in the HIWORD the bitmap to use for
  742.          * the item with the LOWORD containing the indentation.  The
  743.          * bitmap is IDB_FOLDEROPEN for anything above the current
  744.          * directory (that is, c:\foo is above than c:\foo\bar),
  745.          * IDB_FOLDERCLOSED for anything below the current, and
  746.          * IDB_FOLDEROPENSELECT for the current directory.
  747.          */
  748.  
  749.         i=(NULL!=psz) ? IDB_FOLDEROPEN : IDB_FOLDEROPENSELECT;
  750.         SendMessage(hList, LB_SETITEMDATA, iItem, MAKELONG(iItem, i));
  751.  
  752.         if (NULL==psz)
  753.             break;
  754.  
  755.         //Restore last character.
  756.         *psz=ch;
  757.         psz+=(fFirst) ? 0 : 1;
  758.  
  759.         fFirst=FALSE;
  760.         pszLast=psz;
  761.         }
  762.  
  763.  
  764.     /*
  765.      * Now that we have the path in, enumerate the subdirectories here
  766.      * and place them in the list at the indentation iItem+1 since iItem
  767.      * was the index of the last item in the path added to the list.
  768.      *
  769.      * To enumerate the directories, we send LB_DIR to an alternate
  770.      * listbox.  On return, we have to parse off the brackets around
  771.      * those directory names before bringing them into this listbox.
  772.      */
  773.  
  774.     iIndent=iItem+1;
  775.  
  776.     //Get available directories; szDir is pszDir\*.*
  777.     SendMessage(hTempList, LB_DIR, DDL_DIRECTORY | DDL_EXCLUSIVE
  778.                 , (LONG)(LPSTR)szDir);
  779.  
  780.     cItems=(int)SendMessage(hTempList, LB_GETCOUNT, 0, 0L);
  781.  
  782.     for (i=0; i<cItems; i++)
  783.         {
  784.         cch=(UINT)SendMessage(hTempList, LB_GETTEXT, i, (LONG)(LPSTR)szDir);
  785.  
  786.         //Skip directories beginning with . (skipping . and ..)
  787.         if ('.'==szDir[1])
  788.             continue;
  789.  
  790.         //Remove the ending ']'
  791.         szDir[cch-1]=0;
  792.  
  793.         //Add the string to the real directory list.
  794.         iItem=(UINT)SendMessage(hList, LB_ADDSTRING, 0
  795.                                 , (LONG)(LPSTR)(szDir+1));
  796.         SendMessage(hList, LB_SETITEMDATA, iItem
  797.                     , MAKELONG(iIndent, IDB_FOLDERCLOSED));
  798.         }
  799.  
  800.     //Force a listbox repaint.
  801.     SendMessage(hList, WM_SETREDRAW, TRUE, 0L);
  802.     InvalidateRect(hList, NULL, TRUE);
  803.  
  804.     /*
  805.      * If there's a vertical scrollbar, then we've added more items than
  806.      * are visible at once.  To meet the UI specifications, we're supposed
  807.      * to make the next directory up the top visible one.
  808.      */
  809.     GetScrollRange(hList, SB_VERT, (LPINT)&i, (LPINT)&iItem);
  810.  
  811.     if (!(0==i && 0==iItem))
  812.         SendMessage(hList, LB_SETTOPINDEX, max((int)(iIndent-2), 0), 0L);
  813.  
  814.     //Last thing is to set the current directory as the selection
  815.     SendMessage(hList, LB_SETCURSEL, iIndent-1, 0L);
  816.     return;
  817.     }
  818.  
  819.  
  820.  
  821.  
  822.  
  823.  
  824. /*
  825.  * DriveDirDrawItem
  826.  *
  827.  * Purpose:
  828.  *  Handles WM_DRAWITEM for both drive and directory listboxes.
  829.  *
  830.  * Parameters:
  831.  *  pDI             LPDRAWITEMSTRUCT passed with the WM_DRAWITEM message.
  832.  *  fDrive          BOOL TRUE to draw a drive, FALSE to draw directory.
  833.  *
  834.  * Return Value:
  835.  *  None
  836.  */
  837.  
  838. void DriveDirDrawItem(LPDRAWITEMSTRUCT pDI, BOOL fDrive)
  839.     {
  840.     char        szItem[40];
  841.     int         iType=0;
  842.     int         iIndent=0;
  843.     UINT        uMsg;
  844.     DWORD       dw;
  845.     BITMAP      bm;
  846.     COLORREF    crText, crBack;
  847.     HBITMAP     hBmp;
  848.  
  849.     if ((int)pDI->itemID < 0)
  850.         return;
  851.  
  852.     if (fDrive)
  853.         dw=SendMessage(pDI->hwndItem, CB_GETITEMDATA, pDI->itemID, 0L);
  854.  
  855.     //Get the text string for this item (controls have different messages)
  856.     uMsg=(fDrive) ? CB_GETLBTEXT : LB_GETTEXT;
  857.     SendMessage(pDI->hwndItem, uMsg, pDI->itemID, (LONG)(LPSTR)szItem);
  858.  
  859.     if ((ODA_DRAWENTIRE | ODA_SELECT) & pDI->itemAction)
  860.         {
  861.         if (ODS_SELECTED & pDI->itemState)
  862.             {
  863.             //Select the appropriate text colors
  864.             crText=SetTextColor(pDI->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
  865.             crBack=SetBkColor(pDI->hDC, GetSysColor(COLOR_HIGHLIGHT));
  866.             }
  867.  
  868.         /*
  869.          * References to the two bitmap arrays here are the only external
  870.          * dependencies of this code.  To keep it simple, we didn't use
  871.          * a more complex scheme like putting all the images into one bitmap.
  872.          */
  873.         if (fDrive)
  874.             {
  875.             //For drives, get the type, which determines the bitmap.
  876.             iType=(int)HIWORD(dw);
  877.             hBmp=rghBmpDrives[iType-IDB_DRIVEMIN];
  878.             }
  879.         else
  880.             {
  881.             //For directories, indentation level is 4 pixels per indent.
  882.             iIndent=4*(1+LOWORD(pDI->itemData));
  883.             hBmp=rghBmpFolders[HIWORD(pDI->itemData)-IDB_FOLDERMIN];
  884.             }
  885.  
  886.         GetObject(hBmp, sizeof(bm), &bm);
  887.  
  888.         /*
  889.          * Paint the text and the rectangle in whatever colors.  If
  890.          * we're drawing drives, iIndent is zero so it's ineffective.
  891.          */
  892.         ExtTextOut(pDI->hDC, pDI->rcItem.left+bm.bmWidth+4+iIndent
  893.                    , pDI->rcItem.top, ETO_OPAQUE, &pDI->rcItem, szItem
  894.                    , lstrlen(szItem), (LPINT)NULL);
  895.  
  896.  
  897.         //Go draw the bitmap we want.
  898.         TransparentBlt(pDI->hDC, pDI->rcItem.left+iIndent
  899.                        , pDI->rcItem.top, hBmp, RGB(0,0,255));
  900.  
  901.         //Restore original colors if we changed them above.
  902.         if (ODS_SELECTED & pDI->itemState)
  903.             {
  904.             SetTextColor(pDI->hDC, crText);
  905.             SetBkColor(pDI->hDC,   crBack);
  906.             }
  907.         }
  908.  
  909.     if ((ODA_FOCUS & pDI->itemAction) || (ODS_FOCUS & pDI->itemState))
  910.         DrawFocusRect(pDI->hDC, &pDI->rcItem);
  911.  
  912.     return;
  913.     }
  914.  
  915.  
  916.  
  917.  
  918.  
  919.  
  920. /*
  921.  * TransparentBlt
  922.  *
  923.  * Purpose:
  924.  *  Given a DC, a bitmap, and a color to assume as transparent in that
  925.  *  bitmap, BitBlts the bitmap to the DC letting the existing background
  926.  *  show in place of the transparent color.
  927.  *
  928.  * Parameters:
  929.  *  hDC             HDC on which to draw.
  930.  *  x, y            UINT location at which to draw the bitmap
  931.  *  hBmp            HBITMIP to draw
  932.  *  cr              COLORREF to consider as transparent.
  933.  *
  934.  * Return Value:
  935.  *  None
  936.  */
  937.  
  938. void TransparentBlt(HDC hDC, UINT x, UINT y, HBITMAP hBmp, COLORREF cr)
  939.     {
  940.     HDC         hDCSrc, hDCMid;
  941.     HBITMAP     hBmpMono;
  942.     HBRUSH      hBr, hBrT;
  943.     COLORREF    crBack, crText;
  944.     BITMAP      bm;
  945.  
  946.     if (NULL==hBmp)
  947.         return;
  948.  
  949.     GetObject(hBmp, sizeof(bm), &bm);
  950.  
  951.     //Get two intermediate DC's
  952.     hDCSrc=CreateCompatibleDC(hDC);
  953.     hDCMid=CreateCompatibleDC(hDC);
  954.  
  955.     //Create a monochrome bitmap for masking
  956.     hBmpMono =CreateCompatibleBitmap(hDCMid, bm.bmWidth, bm.bmHeight);
  957.  
  958.     SelectObject(hDCSrc, hBmp);
  959.     SelectObject(hDCMid, hBmpMono);
  960.  
  961.     //Create a monochrome mask where we have 0's in the image, 1's elsewhere.
  962.     crBack=SetBkColor(hDCSrc, cr);
  963.     BitBlt(hDCMid, 0, 0, bm.bmWidth, bm.bmHeight, hDCSrc, 0, 0, SRCCOPY);
  964.     SetBkColor(hDCSrc, crBack);
  965.  
  966.     //Put the unmodified image on the screen.
  967.     BitBlt(hDC, x, y, bm.bmWidth, bm.bmHeight, hDCSrc, 0, 0, SRCCOPY);
  968.  
  969.     //Create an select a brush of the background color
  970.     hBr=CreateSolidBrush(GetBkColor(hDC));
  971.     hBrT=SelectObject(hDC, hBr);
  972.  
  973.     //Force conversion of the monochrome to stay black and white.
  974.     crText=SetTextColor(hDC, 0L);
  975.     crBack=SetBkColor(hDC, RGB(255, 255, 255));
  976.  
  977.     /*
  978.      * Where the monochrome mask is 1, Blt the brush; where the mono mask
  979.      * is 0, leave the destination untouches.  This results in painting
  980.      * around the image with the background brush.
  981.      */
  982.     BitBlt(hDC, x, y, bm.bmWidth, bm.bmHeight, hDCMid, 0, 0, ROP_DSPDxax);
  983.  
  984.     SetTextColor(hDC, crText);
  985.     SetBkColor(hDC, crBack);
  986.  
  987.     SelectObject(hDC, hBrT);
  988.     DeleteObject(hBr);
  989.  
  990.     DeleteDC(hDCSrc);
  991.     DeleteDC(hDCMid);
  992.     DeleteObject(hBmpMono);
  993.  
  994.     return;
  995.     }
  996.